package com.eugenema.util;

import com.eugenema.entity.TestStep;
import com.eugenema.enumEntity.Locator;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.*;

/**
 * Selenium 工具类
 * <br/>包括初始化浏览器驱动、关闭浏览器
 *
 * @author Eugenema
 * @date 2023/3/11 21:43
 **/
public class SeleniumUtil {
    private static Logger logger = LoggerFactory.getLogger(SeleniumUtil.class);
    /**
     * 浏览器名称：谷歌浏览器
     */
    public static final String BROWSER_NAME_CHROME = "chrome";
    /**
     * 浏览器名称：火狐浏览器
     */
    public static final String BROWSER_NAME_FIREFOX = "firefox";

    /**
     * 系统参数：谷歌浏览器
     */
    private static final String SYSTEM_PROPERTIES_CHROME_BIN = "webdriver.chrome.bin";
    /**
     * 系统参数：谷歌浏览器驱动
     */
    private static final String SYSTEM_PROPERTIES_CHROME_DRIVER = "webdriver.chrome.driver";
    /**
     * 系统参数：火狐浏览器
     */
    private static final String SYSTEM_PROPERTIES_FIREFOX_BIN = "webdriver.firefox.bin";
    /**
     * 系统参数：火狐浏览器驱动
     */
    private static final String SYSTEM_PROPERTIES_FIREFOX_DRIVER = "webdriver.gecko.driver";

    /**
     * 截图保存路径 File 对象
     */
    private static File screenShotPath = new File(ConfigUtil.getProperty(ConfigUtil.SCREENSHOT_PATH));
    /**
     * 浏览器驱动
     */
    private static WebDriver driver;
    /**
     * 全局存储 map，存储从界面获取到的数据
     */
    public static Map<String, Object> globalMap = new HashMap<>();

    /**
     * 根据配置，获取界面上的元素
     * <br/>约定获取界面元素的描述如下：属性名=属性值
     * <br/>只有通过 id 获取元素才是唯一，其余均可能返回多个元素
     *
     * @param testStep 动作
     * @return 查找到的元素，若没有定位到元素，返回 null
     * @author Eugenema
     * @date 2023/3/11 14:14
     **/
    public static List<WebElement> getElements(TestStep testStep) throws Exception {
        Locator locator = testStep.getLocator();
        String locateValue = testStep.getValue();
        List<WebElement> elementList;
        switch (locator) {
            case ID:
                elementList = getDriver().findElements(By.id(locateValue));
                break;
            case NAME:
                elementList = getDriver().findElements(By.name(locateValue));
                break;
            case XPATH:
                elementList = getDriver().findElements(By.xpath(locateValue));
                break;
            case TAG_NAME:
                elementList = getDriver().findElements(By.tagName(locateValue));
                break;
            case CLASS_NAME:
                elementList = getDriver().findElements(By.className(locateValue));
                break;
            case LINK_TEXT:
                elementList = getDriver().findElements(By.linkText(locateValue));
                break;
            case PARTIAL_LINK_TEXT:
                elementList = getDriver().findElements(By.partialLinkText(locateValue));
                break;
            case CSS_SELECTOR:
                elementList = getDriver().findElements(By.cssSelector(locateValue));
                break;
            default:
                throw new Exception("step 元素 locator 属性配置有误");
        }
        return elementList;
    }

    /**
     * 截图
     * <br/>图片名称格式：毫秒值 + stepId
     * <br/>可用于出错时，或不符合预期值时使用
     * <br/>图片最多保留 SeleniumUtil.MAX_SCREENSHOT_NUM 张
     *
     * @param stepId 步骤 ID
     * @author 马寅广
     * @date 2023/3/10 15:10
     **/
    public static void screenShot(String stepId) {
        //若截图存储路径不存在，则创建
        if (!screenShotPath.exists()) {
            screenShotPath.mkdirs();
        }

        //删除过多的图片，优先删除最早存储的图片
        try {
            File[] pathFiles = screenShotPath.listFiles();
            if (pathFiles.length > Integer.parseInt(ConfigUtil.getProperty(ConfigUtil.SCREENSHOT_MAX_NUM))) {
                logger.info("开始清理旧截图文件……");
                Arrays.sort(pathFiles, Comparator.comparing(File::lastModified));
                for (int i = 0; i < Integer.parseInt(ConfigUtil.getProperty(ConfigUtil.SCREENSHOT_DELETE_NUM)); i++) {
                    pathFiles[i].delete();
                }
                logger.info("清理旧截图文件完成！");
            }
        } catch (Exception exception) {
            logger.error("清理旧截图文件失败：", exception);
        }

        //开始截图
        try {
            File screenShot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);

            String fileName = System.currentTimeMillis() + "_" + stepId + ".png";
            FileUtils.copyFile(screenShot, new File(ConfigUtil.getProperty(ConfigUtil.SCREENSHOT_PATH) + fileName));

            logger.info("步骤【{}】发现问题，截图路径为【{}】", stepId, ConfigUtil.getProperty(ConfigUtil.SCREENSHOT_PATH) + fileName);
        } catch (Exception e) {
            logger.error("步骤【{}】截图失败！", stepId, e);
        }
    }

    /**
     * 初始化浏览器驱动
     *
     * @param browserName 浏览器名称，目前仅支持 firefox 和 chrome
     * @param profile     是否加载个人配置信息
     * @author 马寅广
     * @date 2023/3/10 16:35
     **/
    public static void initBrowserDriver(String browserName, Boolean profile, Boolean noHeadLess) {
        logger.info("开始初始化浏览器驱动【{}浏览器，{}加载个人配置文件，{}台执行】",
                new Object[]{BROWSER_NAME_CHROME.equals(browserName) ? "谷歌" : "火狐", profile ? "" : "不", noHeadLess ? "后" : "前"});
        try {
            if (BROWSER_NAME_FIREFOX.equals(browserName)) {
                getFirefoxDriver(profile);
            } else if (BROWSER_NAME_CHROME.equals(browserName)) {
                getChromeDriver(profile, noHeadLess);
            }
        } catch (Exception exception) {
            logger.error("初始化浏览器驱动异常：", exception);
        }
    }

    /**
     * 获取当前线程对应的浏览器驱动
     *
     * @return 浏览器驱动
     * @author 马寅广
     * @date 2023/3/10 9:50
     **/
    public static WebDriver getDriver() {
        return driver;
    }

    /**
     * 关闭浏览器，driver 置为 null
     *
     * @author 马寅广
     * @date 2023/3/10 16:28
     **/
    public static void quitBrowser() {
        try {
            if (driver == null) {
                return;
            }

            try {
                driver.close();
            } catch (Exception e1) {
                logger.error("浏览器关闭失败，可能已关闭");
            }

            //清理浏览器驱动进程
            Runtime.getRuntime().exec("taskkill /F /IM chromedriver.exe");
            logger.info("脚本执行完成，结束进程并关闭浏览器");
        } catch (Exception e) {
            logger.error("关闭浏览器异常：", e);
        } finally {
            driver = null;
        }
    }

    /**
     * 获取谷歌浏览器驱动
     *
     * @param profile    是否加载个人配置
     * @param noHeadLess 是否无头模式
     * @author 马寅广
     * @date 2023/3/10 14:44
     **/
    private static void getChromeDriver(Boolean profile, Boolean noHeadLess) {
        //设置驱动路径等系统参数
        System.setProperty(SYSTEM_PROPERTIES_CHROME_BIN, ConfigUtil.getProperty(ConfigUtil.BROWSER_CONFIG_CHROME_BIN));
        System.setProperty(SYSTEM_PROPERTIES_CHROME_DRIVER, ConfigUtil.getProperty(ConfigUtil.BROWSER_CONFIG_CHROME_DRIVER));
        //创建启动参数
        ChromeOptions options = new ChromeOptions();
        //加载个人配置
        if (profile) {
            //chrome://version/中的对应个人资料路径
            options.addArguments("user-data-dir=" + ConfigUtil.getProperty(ConfigUtil.BROWSER_CONFIG_CHROME_PROFILE));
        }
        //无头模式启动
        if (noHeadLess) {
            options.addArguments("--headless");
            //禁用 GPU 加速
            options.addArguments("--disable-gpu");
        }
        driver = new ChromeDriver(options);
        driver.manage().window().maximize();
    }

    /**
     * 获取火狐浏览器驱动
     * <br/>全屏显示
     *
     * @param profile 是否加载个人配置信息
     * @author 马寅广
     * @date 2023/3/10 14:39
     **/
    private static void getFirefoxDriver(Boolean profile) {
        System.setProperty(SYSTEM_PROPERTIES_FIREFOX_BIN, ConfigUtil.getProperty(ConfigUtil.BROWSER_CONFIG_FIREFOX_BIN));
        System.setProperty(SYSTEM_PROPERTIES_FIREFOX_DRIVER, ConfigUtil.getProperty(ConfigUtil.BROWSER_CONFIG_FIREFOX_DRIVER));
        if (profile) {
            logger.info("初始化火狐浏览器，加载个人配置信息，耐心等待启动ing...");
            creAppointFirefoxDriver();
        } else {
            logger.info("初始化火狐浏览器，不加载个人配置信息，耐心等待启动ing...");
            creFirefoxDriver();
        }
    }

    /**
     * 创建火狐浏览器驱动
     * <br/>通过获取 geckodriver 路径启动火狐浏览器，不加载用户个人配置。全屏
     *
     * @author 马寅广
     * @date 2023/3/10 11:14
     **/
    private static void creFirefoxDriver() {
        driver = new FirefoxDriver();
        driver.manage().window().maximize();
    }

    /**
     * 创建火狐浏览器驱动
     * <br/>通过获取 geckodriver 路径启动火狐浏览器，加载用户个人配置（包括书签、扩展程序等）。全屏
     *
     * @author 马寅广
     * @date 2023/3/10 11:29
     **/
    private static void creAppointFirefoxDriver() {
        FirefoxOptions options = new FirefoxOptions();
        FirefoxProfile profile = new FirefoxProfile(new File(ConfigUtil.getProperty(ConfigUtil.BROWSER_CONFIG_FIREFOX_PROFILE)));
        options.setProfile(profile);
        driver = new FirefoxDriver(options);
        driver.manage().window().maximize();
    }

}